#pragma once

#include <signal.h>
#include <unistd.h>

#include <cstring>

#include <string>
#include <vector>

#include "socket.hpp"
#include "epoller.hpp"
#include "log.hpp"
extern Log log;

constexpr uint16_t default_port = 8081;

constexpr size_t fd_max_nums = 128;

constexpr uint16_t default_fd = -1;
constexpr uint16_t non_event = 0;

int Receive(int fd)
{
    char buffer[1024] = { 0 };
    ssize_t rn = read(fd, buffer, sizeof(buffer) - 1);
    if (rn > 0)
    {
        buffer[rn - 1] = '\0';
        std::cout << "fd " << fd << " get message: " << buffer << std::endl;
    }
    else if (rn == 0)
        log(Info, "client quit, read done");
    else
        log(Warning, "read error, fd = %d, n = %d, errno: %d, error string: %s", fd, rn, errno, strerror(errno));
    std::string message = "server echo$ ";
    message += buffer;
    message += "\r\n";
    ssize_t wn = write(fd, message.c_str(), message.size());
    if (wn < 0)
        log(Warning, "write error, fd: %d, errno: %d, error string: %s ",  fd, errno, strerror(errno));
    return rn;
}

class Epoll_Server
{
public:
    Epoll_Server(uint16_t port = default_port)
        : _port(port)
    {}

    bool init()
    {
#ifdef __TEST__DEBUG__
        signal(SIGCHLD, SIG_IGN);
#else
        log.change_method(Class);
        daemon(1, 0);
#endif
        _listen_socket.Create(SOCK_STREAM);
        _listen_socket.Bind(_port);
        _listen_socket.Listen();
    }

    void start()
    {
        _epoll_fd.control(_listen_socket.socket_fd(), Epoller::FD_ADD, Epoller::EVENT_IN);

        struct epoll_event revents[Epoller::Size];

        while (true)
        {
            int nums = _epoll_fd.wait(revents, Epoller::Size);
            
            switch (nums)
            {
            case 0:
                std::cout << "time out" << std::endl;
                break;
            case -1:
                std::cerr << "epoll error" << std::endl;
                break;
            default:
                Dispatcher(revents, nums);
                break;
            }
        }
    }

    void Dispatcher(struct epoll_event* revents, int nums)
    {
        for (size_t i = 0; i < nums; ++i)
        {
#ifdef __TEST__DEBUG__
            log(Info, "fd %d has event", revents[i].data.fd);
#endif
            if (revents[i].events & EPOLLIN)
            {
                // 链接fd
                if (revents[i].data.fd == _listen_socket.socket_fd())
                    Accept();
                else
                {
                    // 处理链接fd
                    if (Receive(revents[i].data.fd) == 0)
                    {
                        _epoll_fd.control(revents[i].data.fd, Epoller::FD_DEL, 0);
                        close(revents[i].data.fd);
                    }
                }
            }
        }
    }
private:
    void Accept()
    {
        std::string client_ip;
        uint16_t client_port;
        int fd = _listen_socket.Accept(client_ip, client_port);
        // 添加链接fd
        _epoll_fd.control(fd, Epoller::FD_ADD, Epoller::EVENT_IN);
    }
private:
    Socket _listen_socket;
    uint16_t _port;
    Epoller _epoll_fd;
};